#include "brightness.h"

KBrightness::KBrightness()
{
}

KBrightness::~KBrightness()
{
}

#if defined(Q_OS_LINUX)
static inline double dmin(double a, double b)
{
	return a < b ? a : b;
}

static int findLastNonClamped (uint16_t array[], int size)
{
	int i; 
	for (i = size - 1; i > 0; i--) 
	{
		if (array[i] < 0xffff)
		return i;
	}
	return 0;
}

#elif defined(Q_OS_WIN)
#endif

void KBrightness::setBrightness(int value)
{
#if defined(Q_OS_LINUX)
	qDebug() << "brightness: " << value;
	
	xcb_connection_t *c;
	xcb_generic_error_t *err;
	xcb_screen_t *root;
	int screen = -1;
	char *displayName = NULL;

	displayName = getenv ("DISPLAY");

	/* set up connection */
	c = xcb_connect (displayName, &screen);
	if (xcb_connection_has_error (c)) 
	{
		//fprintf (stderr, "Can't open display %s\n", displayName);
		qDebug() << "Can't open display " << displayName << endl;
		exit (1);
	}
	root = xcb_aux_get_screen (c, screen);

	/* get crtcs */
	xcb_randr_get_screen_resources_current_cookie_t srCookie = xcb_randr_get_screen_resources_current (c, root->root); /* srCookie => screen resources cookie */
	xcb_randr_get_screen_resources_current_reply_t *gsrReply =  xcb_randr_get_screen_resources_current_reply (c, srCookie, &err); /* gsrReply => get screen resources reply */
	xcb_randr_crtc_t *crtc = xcb_randr_get_screen_resources_current_crtcs (gsrReply);

	xcb_randr_get_crtc_gamma_size_cookie_t gsCookie = xcb_randr_get_crtc_gamma_size (c, *crtc); /* gsCookie => gamma size cookie */
	xcb_randr_get_crtc_gamma_size_reply_t *gsReply = xcb_randr_get_crtc_gamma_size_reply (c, gsCookie, &err); /* gsReply => gamma size reply */
	int gammaSize = gsReply->size;

	uint16_t *gammaRamps = new uint16_t[3 * gammaSize];
	uint16_t *gamma_r = &gammaRamps[0 * gammaSize];
	uint16_t *gamma_g = &gammaRamps[1 * gammaSize];
	uint16_t *gamma_b = &gammaRamps[2 * gammaSize];

	float gammaR = 1.0;
	float gammaG = 1.0;
	float gammaB = 1.0;

	int shift = 16 - (ffs(gammaSize) - 1);
	float offset = MAX_BRIGHTNESS - 1;
	float step = (MAX_BRIGHTNESS - MIN_BRIGHTNESS) / 100;
	float brightness = value * step + MIN_BRIGHTNESS;
	qDebug() << "real brightness: " << brightness;

	for (int i = 0; i < gammaSize; i++)
	{
		gamma_r[i] = dmin (pow ((double)i/(double)(gammaSize - 1), gammaR) * brightness, 1.0) * (double)(gammaSize -1);
		gamma_g[i] = dmin (pow ((double)i/(double)(gammaSize - 1), gammaG) * brightness, 1.0) * (double)(gammaSize -1);
		gamma_b[i] = dmin (pow ((double)i/(double)(gammaSize - 1), gammaB) * brightness, 1.0) * (double)(gammaSize -1);
		gamma_r[i] <<= shift;
		gamma_g[i] <<= shift;
		gamma_b[i] <<= shift;
	}
	
	// setting gamma values for each crtc (each monitor)
	for (int j = 0; j < sizeof(crtc) / sizeof(crtc[0]); j++)
	{
		xcb_randr_set_crtc_gamma (c, crtc[j], gammaSize, gamma_r, gamma_g, gamma_b);
	}

	xcb_flush (c);
	delete[] gammaRamps;
	xcb_disconnect (c);

#elif defined(Q_OS_WIN)
	hGDI32 = NULL;
	hGDI32 = ::LoadLibrary("gdi32.dll");
	if (hGDI32 != NULL)
	{
		pSetDeviceGammaRamp = NULL;
		pSetDeviceGammaRamp = (Type_SetDeviceGammaRamp)GetProcAddress(hGDI32, "SetDeviceGammaRamp");
	}
	HDC hGammaDC = NULL;
	hGammaDC = GetDC(NULL);
	if (hGammaDC != NULL)
	{	
		WORD GammaArray[3][256];
		for (int iIndex = 0; iIndex < 256; iIndex++)
		{
			int iArrayValue = iIndex * (value + 128);
			if (iArrayValue > 65535)
				iArrayValue = 65535;
			GammaArray[0][iIndex] = 
				GammaArray[1][iIndex] = 
				GammaArray[2][iIndex] = (WORD)iArrayValue;
		}
		pSetDeviceGammaRamp(hGammaDC,GammaArray);
	}
#endif
}

int KBrightness::getBrightness()
{
#if defined(Q_OS_LINUX)
	xcb_connection_t *c;
	xcb_screen_t *root;
	xcb_generic_error_t *err;
	int screen = -1;
	double brightness;
	char *displayName = NULL;

	/* xcb connect */
	displayName = getenv ("DISPLAY");
	c = xcb_connect (displayName, &screen);
	if (xcb_connection_has_error (c)) 
	{
		//fprintf (stderr, "Can't open display %s\n", displayName);
		qDebug() << "Can't open display " << displayName << endl;
		exit (1);
	}

	/* get get screen */
	root = xcb_aux_get_screen (c, screen);

	/* get screen resources */
	xcb_randr_get_screen_resources_cookie_t srcookie = xcb_randr_get_screen_resources (c, root->root);
	xcb_randr_get_screen_resources_reply_t *gsrReply =  xcb_randr_get_screen_resources_reply (c, srcookie,&err);

	/* get crtc */
	xcb_randr_crtc_t *crtcs = xcb_randr_get_screen_resources_crtcs (gsrReply);

	/* get crtc gamma */
	xcb_randr_get_crtc_gamma_cookie_t gcgCookie = xcb_randr_get_crtc_gamma (c, crtcs[0]);
	xcb_randr_get_crtc_gamma_reply_t *gcgReply = xcb_randr_get_crtc_gamma_reply (c, gcgCookie, &err);

	/* get brightness */

	/* get gamma red, gamma green, gamma blue, those values will be used for calculating the current brightness */
	uint16_t *gammaRed = xcb_randr_get_crtc_gamma_red (gcgReply);
	uint16_t *gammaGreen = xcb_randr_get_crtc_gamma_green (gcgReply);
	uint16_t *gammaBlue = xcb_randr_get_crtc_gamma_blue (gcgReply);

	int size = gcgReply->size;
	int middle, lastBest, lastRed, lastGreen, lastBlue;
	uint16_t *bestArray;
	lastRed = findLastNonClamped (gammaRed, size);
	lastGreen = findLastNonClamped (gammaGreen, size);
	lastBlue = findLastNonClamped (gammaBlue, size);

	bestArray = gammaRed;
	lastBest = lastRed;
	if (lastGreen > lastBest) 
	{
		lastBest = lastGreen;
		bestArray = gammaGreen;
	}

	if (lastBlue > lastBest)
	{
		lastBest = lastBlue;
		bestArray = gammaBlue;
	}

	if (lastBest == 0)
		lastBest = 1;

	middle = lastBest / 2;

	double i1, v1, i2, v2;
	i1 = (double)(middle + 1) / size;
	v1 = (double)(bestArray[middle]) / 65535;
	i2 = (double)(lastBest + 1) / size;
	v2 = (double)(bestArray[lastBest]) / 65535;

	if (v2 < 0.0001) /* The screen is black */
	{
		brightness = 0;
	} else {
		if ((lastBest + 1) == size)
			brightness = v2;
		else 
			brightness = exp((log(v2)*log(i1) - log(v1)*log(i2))/log(i1/i2));
	}

	//delete[] bestArray;
	xcb_disconnect (c);
	qDebug() << "get brightness: " << brightness;
	int brightnessValue = (brightness - MIN_BRIGHTNESS) / (MAX_BRIGHTNESS - MIN_BRIGHTNESS) * 100 + 1;
	if (brightnessValue > 100)
	{
		brightnessValue = 100;
	}

	return brightnessValue;

#elif defined(Q_OS_WIN)
	HDC hDC;
	hDC = GetDC(NULL);
	WORD GammaArray[3][256];
	hGDI32 = ::LoadLibrary("gdi32.dll");
	int error = GetLastError();
	pGetDeviceGammaRamp = (Type_SetDeviceGammaRamp)GetProcAddress(hGDI32, "GetDeviceGammaRamp");
	pGetDeviceGammaRamp(hDC,&GammaArray);
	return (GammaArray[0][1]-128);
#endif
	return 10;
}
